package org.example.order.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import jakarta.annotation.Resource;
import org.example.order.entity.Good;
import org.example.order.entity.Order;
import org.example.order.mapper.GoodMapper;
import org.example.order.mapper.OrderMapper;
import org.example.order.service.RecommendService;
import org.springframework.stereotype.Service;

import java.util.*;

@Service
public class RecommendServiceImpl implements RecommendService {
    @Resource
    private OrderMapper mapper;
    @Resource
    private GoodMapper goodMapper;

    public record GoodSimilarity(Long id, double similarity) {}

    @Override
    public Map<Long, Set<Long>> getUserItem() {
        LambdaQueryWrapper<Order> wrapper = new LambdaQueryWrapper<>();
        wrapper.ne(Order::getStatus, "7");
        List<Order> orders = mapper.selectList(wrapper);
        Map<Long, Set<Long>> userItem = new HashMap<>();
        orders.forEach(order -> {
            long userId = order.getUserId();
            List<Long> items = parseUserItem(order);
            userItem.computeIfAbsent(userId, k -> new HashSet<>());
            items.forEach(item -> userItem.get(userId).add(item));
        });
        return userItem;
    }

    @Override
    public Map<Long, Integer> getItemUserCount(Map<Long, Set<Long>> userItem) {
        Map<Long, Integer> itemUserCount = new HashMap<>();
        userItem.forEach((k, v) -> v.forEach(item -> itemUserCount.compute(item, (kk, vv) -> vv == null ? 1 : vv + 1)));
        return itemUserCount;
    }

    @Override
    // 共现矩阵
    public Map<Long, Map<Long, Integer>> getCoOccurrence(Map<Long, Set<Long>> userItem) {
        Map<Long, Map<Long, Integer>> map = new HashMap<>();
        userItem.forEach((k, v) -> {
            for (long item1 : v) {
                for (long item2 : v) {
                    map.computeIfAbsent(item1, kk -> new HashMap<>())
                        .compute(item2, (kk, vv) -> (vv == null) ? 1 : vv + 1);
                }
            }
        });
        return map;
    }

    @Override
    public Map<Long, Map<Long, Double>> getGoodSimilarities(
        Map<Long, Integer> itemUserCount,
        Map<Long, Map<Long, Integer>> coOccurrence
    ) {
        Map<Long, Map<Long, Double>> similarities = new HashMap<>();
        for (Long id1 : coOccurrence.keySet()) {
            for (Long id2 : coOccurrence.get(id1).keySet()) {
                // Wab = Cab / sqrt(Na * Nb)
                double similarity = coOccurrence.get(id1).get(id2) /
                    Math.sqrt(itemUserCount.get(id1) * itemUserCount.get(id2));
                similarities.computeIfAbsent(id1, kk -> new HashMap<>())
                    .compute(id2, (kk, vv) -> (vv == null) ? similarity : vv);
            }
        }
        return similarities;
    }

    @Override
    public List<Long> getRecommendations(
        long userId,
        int topK,
        Map<Long, Map<Long, Double>> goodSimilarities
    ) {
        List<Order> orders = mapper.selectList(
            new LambdaQueryWrapper<Order>().eq(Order::getUserId, userId)
        );
        Set<Long> items = new HashSet<>();
        orders.forEach(order -> {
            List<Long> userItems = parseUserItem(order);
            items.addAll(userItems);
        });
        // 只推荐上架商品
        List<Long> allGoods = goodMapper.selectByFilter(
                null, null, "1", null, null
            )
            .stream()
            .map(Good::getId)
            .toList();
        List<GoodSimilarity> goodSimilaritiesList = new ArrayList<>();
        for (long id1 : allGoods) {
            if (items.contains(id1)) {
                goodSimilaritiesList.add(new GoodSimilarity(id1, 0.0));
                continue;
            }
            double similaritySum = 0;
            for (long id2 : items) {
                if (!goodSimilarities.containsKey(id1)) {
                    continue;
                }
                similaritySum += goodSimilarities.get(id1).getOrDefault(id2, 0.0);
            }
            goodSimilaritiesList.add(new GoodSimilarity(id1, similaritySum));
        }
        goodSimilaritiesList.sort((a, b) -> Double.compare(b.similarity(), a.similarity()));
        return goodSimilaritiesList.stream()
            .map(GoodSimilarity::id)
            .limit(topK)
            .toList();
    }

    private List<Long> parseUserItem(Order order) {
        List<Long> items = new ArrayList<>();
        String gwc = order.getGwc();
        String[] goodInfos = gwc.split(";");
        for (String goodInfo : goodInfos) {
            String[] parts = goodInfo.split(":");
            if (parts.length != 2) {
                continue;
            }
            items.add(Long.parseLong(parts[0]));
        }
        return items;
    }
}
